package com.telit.firecommand.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hikvision.artemis.sdk.ArtemisHttpUtil;
import com.telit.common.entity.DataReturn;
import com.telit.common.enums.ResultCode;
import com.telit.common.exception.ForestBaseException;
import com.telit.common.util.LanguageUtils;
import com.telit.common.util.RedisUtils;
import com.telit.common.util.TimeUtils;
import com.telit.firecommand.config.properties.FireProperties;
import com.telit.firecommand.constant.DBWConstant;
import com.telit.firecommand.constant.FireConstant;
import com.telit.firecommand.constant.RedisKeyConstant;
import com.telit.firecommand.dao.CameraPresetInfoDao;
import com.telit.firecommand.dao.CommCameraInfoDao;
import com.telit.firecommand.dto.CameraPresetInfoDTO;
import com.telit.firecommand.entity.CameraPresetInfo;
import com.telit.firecommand.entity.CommCameraInfo;
import com.telit.firecommand.feign.FileFeignClient;
import com.telit.firecommand.model.camera.QueryDevicesRequest;
import com.telit.firecommand.service.CameraMonitorService;
import com.telit.firecommand.util.HaikangUtils;
import feign.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * 视频监控操作（调用第三方API）
 *
 * @author yuezhuang
 * @date 2022-08-30
 */
@Slf4j
@Service
public class CameraMonitorServiceImpl implements CameraMonitorService {

    @Autowired
    private FireProperties fireProperties;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private CameraPresetInfoDao cameraPresetInfoDao;

    @Autowired
    private CommCameraInfoDao commCameraInfoDAO;

    @Autowired
    private FileFeignClient fileFeignClient;

    @Value("${forest.communicate.url}")
    private String communicateUrl;

    /**
     * 获取监控点预览取流URL
     *
     * @param indexCode 监控点唯一标识
     *
     * @return 取流URL
     */
    @Override
    public String queryRtspUrl(String indexCode) {
        String url = fireProperties.getDbw().getApiUrl() + DBWConstant.RTSP_URL;
        String token = fireProperties.getDbw().getToken();
        String host = fireProperties.getDbw().getHost();
        //protocol:HTTP(http-flv),WS(ws-flv)
        String protocol = "HTTP";
        String param = "?token=" + token + "&host=" + host + "&protocol=" + protocol + "&channelId=" + indexCode;

        JSONObject jsonObject = restTemplate.postForObject(url + param, null, JSONObject.class);

        if (!jsonObject.getBoolean("success")) {
            throw new ForestBaseException(jsonObject.get("msg").toString());
        }

        return jsonObject.getString("data");
    }

    /**
     * 获取监控点预览取流flv URL
     *
     * @param indexCode 监控点唯一标识
     *
     * @return 取流URL
     */
    @Override
    public String queryFlvpUrl(String indexCode) {
        String url=fireProperties.getDbw().getApiUrl()+ DBWConstant.PREVIEW_URL;
        String token=fireProperties.getDbw().getToken();
        String host=fireProperties.getDbw().getHost();
        //protocol:HTTP(http-flv),WS(ws-flv)
        String protocol="WS";
        String param="?token="+token+"&host="+host+"&protocol="+protocol+"&channelId="+indexCode;

        JSONObject jsonObject=restTemplate.postForObject(url+param,null,JSONObject.class);

        if (!jsonObject.getBoolean("success")) {
            throw new ForestBaseException(jsonObject.get("msg").toString());
        }

        String result=jsonObject.getString("data");

        if(!StringUtils.isEmpty(result)&&!StringUtils.isEmpty(fireProperties.getDbw().getTransmit())){
            String oldUrl=fireProperties.getDbw().getProtocol()+fireProperties.getDbw().getHost()+":"+fireProperties.getDbw().getPort();
            String newUrl=fireProperties.getDbw().getTransmit();
            result=result.replaceAll(oldUrl,newUrl);
        }

        return result;
    }

    /**
     * 分页获取监控点资源
     *
     * @param queryDevicesRequest QueryDevicesRequest
     *
     * @return String
     */
    @Override
    public List<Map> queryDevices(QueryDevicesRequest queryDevicesRequest) {
        String url = fireProperties.getDbw().getApiUrl() + DBWConstant.URL_DEVICE;
        String token = fireProperties.getDbw().getToken();
        String host = fireProperties.getDbw().getHost();
        //protocol:HTTP(http-flv),WS(ws-flv)
        String protocol = "HTTP";
        String param = "?token=" + token + "&host=" + host + "&protocol=" + protocol;

        JSONObject jsonObject = restTemplate.postForObject(url + param, null, JSONObject.class);

        if (!jsonObject.getBoolean("success")) {
            throw new ForestBaseException(jsonObject.get("msg").toString());
        }
        List<Map> data = JSON.parseArray(JSON.toJSONString(jsonObject.getJSONArray("data")), Map.class);
        return data;
    }

    /**
     * h5登录
     *
     * @param user     用户
     * @param password 密码
     *
     * @return h5登录链接
     */
    @Override
    public Object h5Login(String user, String password) {
        return restTemplate.getForObject(
                fireProperties.getH5().getLoginUrl() + "?user=" + user + "&password=" + password, Object.class);
    }

    /**
     * h5保活接口
     */
    @Override
    public Object h5KeepAlive(String session) {
        return restTemplate.getForObject(fireProperties.getH5().getKeepAliveUrl() + "?session=" + session,
                Object.class);
    }

    /**
     * h5云台控制
     */
    @Override
    public Object ptzCtl(String token, String action, String speed, String session) {
        return restTemplate.getForObject(
                fireProperties.getH5().getPtzUrl() + "?session=" + session + "&token=" + token + "&action=" + action
                        + "&speed=" + speed, Object.class);
    }

    @Override
    @Transactional
    public String setPresets(Long cameraId, String indexCode, String presetName, Integer presetIndex,
                             Integer standTime, Integer speed) {
        QueryWrapper<CameraPresetInfo> wrapper = new QueryWrapper<>();
        if (cameraId != null) {
            wrapper.eq("camera_id", cameraId);
        }
        if (StrUtil.isNotEmpty(indexCode)) {
            wrapper.eq("camera_index_code", indexCode);
        }
        wrapper.eq("preset_name", presetName);
        CameraPresetInfo cameraPresetInfo = cameraPresetInfoDao.selectOne(wrapper);
        if (cameraPresetInfo == null) {
            cameraPresetInfo = new CameraPresetInfo();
            cameraPresetInfo.setCameraId(cameraId);
            cameraPresetInfo.setPresetIndex(presetIndex);
            cameraPresetInfo.setPresetName(presetName);
            cameraPresetInfo.setStandTime(standTime);
            cameraPresetInfo.setSpeed(speed);
            cameraPresetInfoDao.insertSelective(cameraPresetInfo);
        } else {
            cameraPresetInfo.setPresetIndex(presetIndex);
            cameraPresetInfo.setPresetName(presetName);
            cameraPresetInfo.setStandTime(standTime);
            cameraPresetInfo.setSpeed(speed);
            cameraPresetInfoDao.updateById(cameraPresetInfo);
        }

//        String url = fireProperties.getDbw().getApiUrl() + DBWConstant.SET_PRESETS;
//        String token = fireProperties.getDbw().getToken();
//        String param = "?token=" + token + "&channelId=" + indexCode + "&index=" + presetIndex + "&id="
//                + cameraPresetInfo.getId() + "&duration=" + standTime + "&speed=" + speed;
//        JSONObject jsonObject = restTemplate.postForObject(url + param, null, JSONObject.class);
//        if (jsonObject.getIntValue("code") != 200 || !jsonObject.getBoolean("success")) {
//            throw new ForestBaseException(jsonObject.get("msg").toString());
//        }

        return "success";
    }

    @Override
    public String delPresets(Long cameraId, String indexCode, Integer presetIndex, Long id) {
        String url = fireProperties.getDbw().getApiUrl() + DBWConstant.DEL_PRESETS;
        String token = fireProperties.getDbw().getToken();
        String param = "?token=" + token + "&channelId=" + indexCode + "&index=" + presetIndex + "&id=" + id;
        JSONObject jsonObject = restTemplate.postForObject(url + param, null, JSONObject.class);
        if (jsonObject.getIntValue("code") != 200 || !jsonObject.getBoolean("success") || !jsonObject.getBoolean(
                "data")) {
            throw new ForestBaseException(jsonObject.get("msg").toString());
        }

        //删除预置点
//        QueryWrapper<CameraPresetInfo> wrapper = new QueryWrapper<>();
//        wrapper.eq("camera_id", cameraId);
//        wrapper.eq("preset_index", presetIndex);
//        wrapper.eq("id", id);
//        cameraPresetInfoDao.delete(wrapper);

        return "success";
    }

    @Override
    public void delAllPresets(Long cameraId, String indexCode) {
        List<CameraPresetInfoDTO> cameraPresetInfoDTOS = searchPresets(cameraId, indexCode);
        cameraPresetInfoDTOS.forEach(cameraPresetInfoDTO -> {
            try {
                delPresets(cameraId, indexCode, 0, cameraPresetInfoDTO.getId());
            } catch (Exception e) {
                log.error("删除预置点失败：", e);
            }
        });

//        QueryWrapper<CameraPresetInfo> wrapper = new QueryWrapper<>();
//        wrapper.eq("camera_id", cameraId);
//        List<CameraPresetInfo> cameraPresetInfos = cameraPresetInfoDao.selectList(wrapper);
//        cameraPresetInfos.forEach(e -> {
//            Integer presetIndex = e.getPresetIndex();
//            Map<String, String> url = HaikangUtils.getUrl(HaikangUtils.DEL_PRESETS);
//
//            String mime = HaikangUtils.MIME;
//
//            JSONObject jsonBody = new JSONObject();
//            jsonBody.put("cameraIndexCode", indexCode);
//            jsonBody.put("presetIndex", presetIndex);
//            String body = jsonBody.toJSONString();
//
//            String result = ArtemisHttpUtil.doPostStringArtemis(url, body, null, null, mime, null);
//            log.info("海康DEL_PRESET请求结果:" + result);
//
//            cameraPresetInfoDao.deleteById(e.getId());
//        });
    }

    @Override
    public List<CameraPresetInfoDTO> searchPresets(Long cameraId, String indexCode) {
        LambdaQueryWrapper<CommCameraInfo> commCameraInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (cameraId != null) {
            commCameraInfoLambdaQueryWrapper.eq(CommCameraInfo::getId, cameraId);
        }
        if (StrUtil.isNotEmpty(indexCode)) {
            commCameraInfoLambdaQueryWrapper.eq(CommCameraInfo::getCameraIndexCode, indexCode);
        }
        CommCameraInfo commCameraInfo = commCameraInfoDAO.selectOne(commCameraInfoLambdaQueryWrapper);
        if (commCameraInfo == null) {
            throw new ForestBaseException(LanguageUtils.codeMessage(ResultCode.OPERATE_ERROR.getCode()));
        }

        String url = fireProperties.getDbw().getApiUrl() + DBWConstant.SEARCH_PRESETS;
        String token = fireProperties.getDbw().getToken();
        String param = "?token=" + token + "&channelId=" + commCameraInfo.getCameraIndexCode() + "&index=" + 0;
        JSONObject jsonObject = restTemplate.postForObject(url + param, null, JSONObject.class);
        if (jsonObject.getIntValue("code") != 200 || !jsonObject.getBoolean("success")) {
            log.error("查询预置点失败：{}", JSON.toJSONString(jsonObject));
            return new ArrayList<>();
//            throw new ForestBaseException(jsonObject.get("msg").toString());
        }

        JSONArray data = jsonObject.getJSONArray("data");
        if (data == null) {
            return new ArrayList<>();
        }
        List<CameraPresetInfoDTO> cameraPresetInfoDTOS = new ArrayList<>();
        for (int i = 0; i < data.size(); i++) {
            JSONObject json = data.getJSONObject(i);
            cameraPresetInfoDTOS.add(CameraPresetInfoDTO.builder()
                    .id(json.getLong("id"))
                    .cameraId(cameraId)
                    .presetName(json.getString("name"))
                    .presetIndex(json.getInteger("index"))
                    .standTime(json.getInteger("duration"))
                    .speed(json.getInteger("speed"))
                    .set(json.getBooleanValue("set"))
                    .build());
        }
        return cameraPresetInfoDTOS;
    }

    /**
     * 根据监控点编号进行云台操作
     *
     * @param indexCode   监控点唯一标识，分页获取监控点资源接口获取返回参数cameraIndexCode
     * @param presetIndex 预置点编号，整数，通常在300以内
     *
     * @return String
     */
    @Override
    public String goToPresets(String indexCode, Integer presetIndex) {
        Map<String, String> url = HaikangUtils.getUrl(HaikangUtils.PTZ_CONTROL);

        String mime = HaikangUtils.MIME;

        JSONObject jsonBody = new JSONObject();
        jsonBody.put("cameraIndexCode", indexCode);
        jsonBody.put("action", 1);
        jsonBody.put("command", "GOTO_PRESET");
        jsonBody.put("presetIndex", presetIndex);
        String body = jsonBody.toJSONString();

        String result = ArtemisHttpUtil.doPostStringArtemis(url, body, null, null, mime, null);
        log.info("海康PTZ_CONTROL请求结果 :" + result);

        return result;
    }

    /**
     * 根据监控点编号进行云台操作
     *
     * @param indexCode（channelId） 监控点唯一标识，分页获取监控点资源接口获取返回参数cameraIndexCode
     * @param action（flag）         true-开始 false-停止 注：GOTO_PRESET命令下填任意值均可转到预置点,建议填0即可
     * @param command              不区分大小写
     *                             说明：
     *                             LEFT（left） 左转
     *                             RIGHT（right）右转
     *                             UP（top） 上转
     *                             DOWN（bottom） 下转
     *                             ZOOM_IN（focusIn） 焦距变大
     *                             ZOOM_OUT（focusOut） 焦距变小
     *                             LEFT_UP（topLeft） 左上
     *                             LEFT_DOWN（bottomLeft） 左下
     *                             RIGHT_UP（topRight） 右上
     *                             RIGHT_DOWN（bottomRight） 右下 FOCUS_NEAR 焦点前移
     *                             FOCUS_FAR 焦点后移 IRIS_ENLARGE（apertureIn） 光圈扩大
     *                             IRIS_REDUCE（apertureOut） 光圈缩小
     *                             WIPER_SWITCH 接通雨刷开关
     *                             START_RECORD_TRACK 开始记录轨迹
     *                             STOP_RECORD_TRACK 停止记录轨迹
     *                             START_TRACK 开始轨迹
     *                             STOP_TRACK 停止轨迹
     *
     *                             以下命令presetIndex不可
     *                             为空：
     *                             GOTO_PRESET（preset）到预置点
     * @param speed                云台速度，取值范围为1-100，默认50
     * @param presetIndex          预置点编号，整数，通常在300以内
     * @param cruiseIndex          自动巡航控制时有效
     *
     * @return String
     */
    @Override
    public String ptzControl(String indexCode, Integer action, String command, Integer speed, Integer presetIndex,
                             Integer cruiseIndex) {
        command = DBWConstant.COMMAND_MAP.get(command);
        if (StrUtil.isEmpty(command)) {
            throw new ForestBaseException("不支持当前指令！");
        }

        String url = fireProperties.getDbw().getApiUrl() + DBWConstant.PTZ_CONTROL;
        String token = fireProperties.getDbw().getToken();
        String host = fireProperties.getDbw().getHost();
        String protocol = "HTTP";
        String param = "?token=" + token + "&host=" + host + "&protocol=" + protocol + "&channelId=" + indexCode;
        boolean flag = false;
        if (action == 0) {
            flag = true;
        }
        param += "&command=" + command;
        param += "&flag=" + flag;
        param += "&speed=" + speed;
        if (presetIndex != null) {
            param += "&presetIndex=" + presetIndex;
        }
        if (cruiseIndex != null) {
            param += "&cruiseIndex=" + cruiseIndex;
        }

        JSONObject jsonObject = restTemplate.postForObject(url + param, null, JSONObject.class);

        if (!jsonObject.getBoolean("success")) {
            throw new ForestBaseException(jsonObject.get("msg").toString());
        }

        return jsonObject.getString("data");
    }

    /**
     * 查询巡航路径
     *
     * @param indexCode 监控点唯一标识,从分页获取监控点资源接口获取返回参数cameraIndexCode
     * @param nameLike  巡航路径名称，名称模糊搜索，为空或者为null时忽略此条件
     *
     * @return String
     */
    @Override
    public String searchRoutes(String indexCode, String nameLike) {
        Map<String, String> url = HaikangUtils.getUrl(HaikangUtils.SEARCH_ROUTES);

        String mime = HaikangUtils.MIME;

        JSONObject jsonBody = new JSONObject();
        jsonBody.put("cameraIndexCode", indexCode);
        if (nameLike != null && !"".equals(nameLike)) {
            jsonBody.put("nameLike", nameLike);
        }
        String body = jsonBody.toJSONString();

        String result = ArtemisHttpUtil.doPostStringArtemis(url, body, null, null, mime, null);
        log.info("海康SEARCH_ROUTES请求结果 :" + result);

        return result;
    }

    /**
     * 添加或修改巡航路径
     *
     * @param cruiseRoutes 请求参数json
     *
     * @return String
     */
    @Override
    public String updateRoutes(String cruiseRoutes) {
        Map<String, String> url = HaikangUtils.getUrl(HaikangUtils.UPDATE_ROUTES);

        String mime = HaikangUtils.MIME;

        String result = ArtemisHttpUtil.doPostStringArtemis(url, cruiseRoutes, null, null, mime, null);
        log.info("海康UPDATE_ROUTES请求结果 :" + result);

        return result;
    }

    /**
     * 删除巡航路径
     *
     * @param indexCode   监控点唯一标识,从分页获取监控点资源接口获取返回参数cameraIndexCode
     * @param cruiseIndex 巡航路径序号，范围：[1,8]，可通过查询巡航路径接口获取返回参数cruiseIndex
     *
     * @return String
     */
    @Override
    public String deleteRoutes(String indexCode, Integer cruiseIndex) {
        Map<String, String> url = HaikangUtils.getUrl(HaikangUtils.DELETE_ROUTES);

        String mime = HaikangUtils.MIME;

        JSONObject jsonBody = new JSONObject();
        jsonBody.put("cameraIndexCode", indexCode);
        jsonBody.put("cruiseIndex", cruiseIndex);
        String body = jsonBody.toJSONString();

        String result = ArtemisHttpUtil.doPostStringArtemis(url, body, null, null, mime, null);
        log.info("海康DELETE_ROUTES请求结果 :" + result);

        return result;
    }

    /**
     * 巡航控制
     *
     * @param indexCode      监控点唯一标识,从分页获取监控点资源接口获取返回参数cameraIndexCode
     * @param monitoringType 1-和普威视监控设备
     * @param command        控制命令，不区分大小写，开始巡航：START_CRUISE；停止巡航：STOP_CRUISE
     * @param cruiseIndex    巡航路径序号，范围：[1,8]，可通过查询巡航路径接口获取返回参数cruiseIndex
     *
     * @return String
     */
    @Override
    public String controlRoutes(String indexCode, String monitoringType, String command, Integer cruiseIndex) {
        String result = "{\"code\":\"1\",\"msg\":\"fail\",\"data\":null}";
        if ("1".equals(monitoringType)) {
            //和普威视监控设备
            if ("START_CRUISE".equals(command)) {
                //开始巡航
                Map<String, String> url = HaikangUtils.getUrl(HaikangUtils.PTZ_CONTROL);
                String mime = HaikangUtils.MIME;
                JSONObject jsonBody = new JSONObject();
                jsonBody.put("cameraIndexCode", indexCode);
                jsonBody.put("action", 1);
                jsonBody.put("command", "GOTO_PRESET");
                jsonBody.put("presetIndex", "98");
                String body = jsonBody.toJSONString();
                result = ArtemisHttpUtil.doPostStringArtemis(url, body, null, null, mime, null);
                log.info("海康PTZ_CONTROL请求结果 :" + result);

            } else if ("STOP_CRUISE".equals(command)) {
                //结束巡航
                Map<String, String> url = HaikangUtils.getUrl(HaikangUtils.PTZ_CONTROL);
                String mime = HaikangUtils.MIME;
                JSONObject jsonBody = new JSONObject();
                jsonBody.put("cameraIndexCode", indexCode);
                jsonBody.put("action", 1);
                jsonBody.put("command", "GOTO_PRESET");
                jsonBody.put("presetIndex", "110");
                String body = jsonBody.toJSONString();
                result = ArtemisHttpUtil.doPostStringArtemis(url, body, null, null, mime, null);
                log.info("海康PTZ_CONTROL请求结果 :" + result);
            }
        } else {
            //海康监控设备
            Map<String, String> url = HaikangUtils.getUrl(HaikangUtils.CONTROL_ROUTES);
            String mime = HaikangUtils.MIME;
            JSONObject jsonBody = new JSONObject();
            jsonBody.put("cameraIndexCode", indexCode);
            jsonBody.put("command", command);
            jsonBody.put("cruiseIndex", cruiseIndex);
            String body = jsonBody.toJSONString();
            result = ArtemisHttpUtil.doPostStringArtemis(url, body, null, null, mime, null);
            log.info("海康CONTROL_ROUTES请求结果 :" + result);
        }
        return result;
    }

    /**
     * 获取监控点预览取流flv URL
     *
     * @param cameraIndexCode 监控点唯一标识
     * @param beginTime       开始时间
     * @param endTime         结束时间
     * @param protocol        取流协议（应用层协议)，
     *                        “rtsp”:RTSP协议（标准rtsp协议常用于如开源工具vlc取流播放，非标rtsp协议一般用于对接海康sdk。标准rtsp协议与非标rtsp协议如何指定请参考expand字段。）；
     *                        “rtmp”:RTMP协议（RTMP协议只支持海康SDK协议、EHOME协议、ONVIF协议接入的设备；只支持H264视频编码和AAC音频编码；RTMP回放要求录像片段连续，需要在URL后自行拼接beginTime=20190902T100303&endTime=20190902T100400，其中20190902T100303至20190902T100400为查询出有连续录像的时间段。对于不连续的录像，需要分段查询分段播放）；
     *                        “hls”:HLS协议（一般用于H5页面取流播放，HLS协议只支持海康SDK协议、EHOME协议、ONVIF协议接入的设备；只支持H264视频编码和AAC音频编码；hls协议只支持云存储，不支持设备存储，云存储版本要求v2.2.4及以上的2.x版本，或v3.0.5及以上的3.x版本需在运管中心-视频联网共享中切换成启动平台外置VOD）；
     *                        “ws”:Websocket协议（一般用于H5视频播放器取流播放）。此字段未指定默认为rtsp协议
     *                        "httpflv"、“httpsflv”:海康的http-flv取流地址
     *                        “HTTP”:东巴文http-flv取流地址
     *                        “WS”:东巴文ws-flv取流地址
     * @param recordLocation  录像存储位置（0-中心存储，1-设备存储）,默认中心存储
     *
     * @return String
     */
    @Override
    public String playbackURLs(String cameraIndexCode, String beginTime, String endTime, String protocol,
                               String recordLocation) {
        String url = fireProperties.getDbw().getApiUrl() + DBWConstant.PLAY_BACK;
        String token = fireProperties.getDbw().getToken();
        String host = fireProperties.getDbw().getHost();
        //protocol:HTTP(http-flv),WS(ws-flv)
        if(StringUtils.isEmpty(protocol)){
            protocol = "WS";
        }
        String param = "?token=" + token + "&host=" + host + "&protocol=" + protocol + "&channelId=" + cameraIndexCode
                + "&start=" + beginTime + "&end=" + endTime;

        JSONObject jsonObject = restTemplate.postForObject(url + param, null, JSONObject.class);

        if (!jsonObject.getBoolean("success")) {
            throw new ForestBaseException(jsonObject.get("msg").toString());
        }

        return jsonObject.getString("data");
    }

    // ================================== 以下接口调用windows服务 ================================

    /**
     * 视频下载
     *
     * @param userId          用户id
     * @param cameraIndexCode 监控点唯一标识
     * @param beginTime       开始时间
     * @param endTime         结束时间
     * @param recordLocation  录像存储位置（0-中心存储，1-设备存储）,默认中心存储
     *
     * @return DataReturn Obj
     */
    @Override
    public DataReturn downloadVideo(String userId, String cameraIndexCode, String beginTime, String endTime,
                                    String recordLocation) {
        //解决海康平台bug：频繁调用 /artemis/api/video/v1/cameras/playbackURLs 接口后，无法下载视频。
        //两次相同时间段取流时间间隔应当大于10秒钟
        String key =
                RedisKeyConstant.CAMERA_PLAYBACK_URL + cameraIndexCode + "_" + TimeUtils.convertTimeStamp(beginTime)
                        + "_" + TimeUtils.convertTimeStamp(endTime);
        //上次取流时间
        String value = RedisUtils.get(key);
        if (StringUtils.isNotEmpty(value)) {
            //当前时间
            long current = new Date().getTime();
            //差
            long diff = current - Long.parseLong(value);
            //小于10秒时，睡到10秒
            long sleepTime = FireConstant.CAMERA_PLAYBACK_URL_PERIOD * 1000 - diff;
            if (sleepTime > 0) {
                //线程睡眠到10秒
                try {
                    TimeUnit.MILLISECONDS.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        return fileFeignClient.downloadVideo(userId, cameraIndexCode, beginTime, endTime, recordLocation,
                communicateUrl + "/communicate/msg/send");
    }

    /**
     * 查询是否下载完成
     *
     * @param userId          用户id
     * @param cameraIndexCode 监控点编号
     *
     * @return DataReturn Obj
     */
    @Override
    public DataReturn queryIsDownloadByCameraId(String userId, String cameraIndexCode) {
        return fileFeignClient.queryIsDownloadByCameraId(userId, cameraIndexCode);
    }

    /**
     * 下载文件
     *
     * @param fileName 文件名称
     * @param response 输出流
     */
    @Override
    public void download(String fileName, HttpServletResponse response) {
        InputStream is = null;
        try {
            Response feignResponse = fileFeignClient.download(fileName);
            Response.Body body = feignResponse.body();
            is = body.asInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);
            response.setHeader("Content-Disposition",
                    feignResponse.headers().get("content-disposition").toString().replace("[", "").replace("]", ""));
            response.setHeader("Content-Length",
                    feignResponse.headers().get("content-length").toString().replace("[", "").replace("]", ""));
            BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
            int length = 0;
            byte[] temp = new byte[1024 * 10];
            while ((length = bis.read(temp)) != -1) {
                bos.write(temp, 0, length);
            }
            bos.flush();
            bos.close();
            bis.close();
            is.close();
        } catch (Exception e) {
            log.error("文件下载失败");
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名称
     *
     * @return DataReturn Obj
     */
    @Override
    public DataReturn delete(String fileName) {
        return fileFeignClient.delete(fileName);
    }

    /**
     * 订阅火情事件
     * @param callbackUrl
     * @param eventTypes
     * @return
     */
    @Override
    public String subscribeEvent(String callbackUrl, int[] eventTypes) {
        Map<String, String> url = new HashMap<>();
        url.put("https://", HaikangUtils.SUBSCRIBE_EVENT);
        String mime = HaikangUtils.MIME;

        JSONObject jsonBody = new JSONObject();
        jsonBody.put("eventTypes", eventTypes);
        jsonBody.put("eventDest", callbackUrl);
        jsonBody.put("subType", 0);
        String body = jsonBody.toJSONString();
        String result = ArtemisHttpUtil.doPostStringArtemis(url, body, null, null, mime, null);
        return result;
    }

    /**
     * 取消订阅火情事件
     * @param eventTypes
     * @return
     */
    @Override
    public String unSubscribeEvent(int[] eventTypes) {
        Map<String, String> url = new HashMap<>();
        url.put("https://", HaikangUtils.UNSUBSCRIBE_EVENT);
        String mime = HaikangUtils.MIME;

        JSONObject jsonBody = new JSONObject();
        jsonBody.put("eventTypes", eventTypes);
        String body = jsonBody.toJSONString();
        String result = ArtemisHttpUtil.doPostStringArtemis(url, body, null, null, mime, null);
        return result;
    }

    /**
     * 查询订阅火情事件
     * @return
     */
    @Override
    public String viewEvent() {
        Map<String, String> url = new HashMap<>();
        url.put("https://", HaikangUtils.VIEW_EVENT);
        String mime = HaikangUtils.MIME;

        String result = ArtemisHttpUtil.doPostStringArtemis(url, null, null, null, mime, null);

        return result;
    }

    /**
     * 将预置点添加到巡航路线中
     */
    @Override
    public void setToCruise(Long cameraId, String indexCode, Integer presetIndex, Long id, Integer standTime,
                            Integer speed) {
        LambdaQueryWrapper<CommCameraInfo> commCameraInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (cameraId != null) {
            commCameraInfoLambdaQueryWrapper.eq(CommCameraInfo::getId, cameraId);
        }
        if (StrUtil.isNotEmpty(indexCode)) {
            commCameraInfoLambdaQueryWrapper.eq(CommCameraInfo::getCameraIndexCode, indexCode);
        }
        CommCameraInfo commCameraInfo = commCameraInfoDAO.selectOne(commCameraInfoLambdaQueryWrapper);
        if (commCameraInfo == null) {
            throw new ForestBaseException(LanguageUtils.codeMessage(ResultCode.OPERATE_ERROR.getCode()));
        }

        String url = fireProperties.getDbw().getApiUrl() + DBWConstant.SET_PRESETS;
        String token = fireProperties.getDbw().getToken();
        String param =
                "?token=" + token + "&channelId=" + indexCode + "&index=" + presetIndex + "&id=" + id + "&duration="
                        + standTime + "&speed=" + speed;
        JSONObject jsonObject = restTemplate.postForObject(url + param, null, JSONObject.class);
        if (jsonObject.getIntValue("code") != 200 || !jsonObject.getBoolean("success") || !jsonObject.getBoolean(
                "data")) {
            throw new ForestBaseException(jsonObject.get("msg").toString());
        }
    }

}
